package com.plugins.mybatis;

import org.mybatis.generator.api.*;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.codegen.XmlConstants;
import org.mybatis.generator.config.PropertyRegistry;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import static org.mybatis.generator.internal.util.messages.Messages.getString;

/**
 * 自定义最终生成的文件的样式
 *
 * @author gang
 */
public class LoanTemplatePlugin extends PluginAdapter {

    private static final String MAPPER_PATH = "com.core.data.IMapper";

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass,
                                   IntrospectedTable introspectedTable) {
        boolean isPresent;
        try {
            Class.forName(MAPPER_PATH);
            isPresent = true;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            isPresent = false;
        }
        if (isPresent) {
            interfaze.addImportedType(new FullyQualifiedJavaType(MAPPER_PATH));
            String modelName = introspectedTable.getFullyQualifiedTable().getDomainObjectName();
            String superName = "IMapper<" + modelName + ">";
            interfaze.addSuperInterface(new FullyQualifiedJavaType(superName));
        }
        return true;
    }

    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        List<GeneratedJavaFile> answer = new ArrayList<>();

        Interface extMapperInterface = createExtMapperInterface(introspectedTable);
        Interface serviceInterface = createServiceInterface(introspectedTable);
        TopLevelClass implClass = createServiceImplClass(serviceInterface, extMapperInterface);

        addIfNotExists(extMapperInterface, answer);
        addIfNotExists(serviceInterface, answer);
        addIfNotExists(implClass, answer);
        return answer;
    }

    public List<GeneratedXmlFile> contextGenerateAdditionalXmlFiles(IntrospectedTable introspectedTable) {
        List<GeneratedXmlFile> answer = new ArrayList<>();
        GeneratedXmlFile generatedXmlFile = createExtMapperGeneratedXmlFile(introspectedTable);
        addIfNotExists(generatedXmlFile, answer);
        return answer;
    }

    private GeneratedXmlFile createExtMapperGeneratedXmlFile(IntrospectedTable introspectedTable) {
        XmlElement xmlRoot = createXmlElement(introspectedTable);
        Document document = getDocument(xmlRoot, introspectedTable);

        return new GeneratedXmlFile(document,
                getMyBatis3XmlExtMapperFileName(introspectedTable), getMyBatis3XmlMapperPackage(introspectedTable),
                context.getSqlMapGeneratorConfiguration().getTargetProject(),
                true, context.getXmlFormatter());
    }


    public Document getDocument(XmlElement rootElement, IntrospectedTable introspectedTable) {
        Document document = new Document(
                XmlConstants.MYBATIS3_MAPPER_PUBLIC_ID,
                XmlConstants.MYBATIS3_MAPPER_SYSTEM_ID);
        document.setRootElement(rootElement);

        if (!context.getPlugins().sqlMapDocumentGenerated(document,
                introspectedTable)) {
            document = null;
        }
        return document;
    }


    private XmlElement createXmlElement(IntrospectedTable introspectedTable) {
        XmlElement answer = new XmlElement("mapper");
        String namespace = introspectedTable.getMyBatis3SqlMapNamespace();
        namespace = namespace.replace("Mapper", "ExtMapper");
        answer.addAttribute(new Attribute("namespace",
                namespace));

        XmlElement baseResultMapElement = createExtResultMapElement(introspectedTable);
        answer.addElement(baseResultMapElement);
        TextElement commentElement = createBaseColumnListComment(introspectedTable);
        answer.addElement(commentElement);
        context.getCommentGenerator().addRootComment(answer);

        return answer;
    }

    /**
     * 创建extMapper的BaseResultMap
     */
    private XmlElement createExtResultMapElement(IntrospectedTable introspectedTable) {
        XmlElement answer = new XmlElement("resultMap");
        String BaseResultMapId = introspectedTable.getBaseResultMapId();
        String javaMapperType = introspectedTable.getMyBatis3JavaMapperType();

        answer.addAttribute(new Attribute("id",
                BaseResultMapId));
        answer.addAttribute(new Attribute("type",
                introspectedTable.getBaseRecordType()));
        answer.addAttribute(new Attribute("extends", javaMapperType + "." + BaseResultMapId));

        return answer;
    }

    private TextElement createBaseColumnListComment(IntrospectedTable introspectedTable) {
        String javaMapperType = introspectedTable.getMyBatis3JavaMapperType();
        String baseColumnListId = introspectedTable.getBaseColumnListId();
        String commentFormat = "    <!--    <include refid=\"%s\"/>-->";
        TextElement element = new TextElement(String.format(commentFormat, javaMapperType + "." + baseColumnListId));
        return element;
    }

    private String getMyBatis3XmlExtMapperFileName(IntrospectedTable introspectedTable) {
        String mapperFileName = introspectedTable.getMyBatis3XmlMapperFileName();
        return mapperFileName.replace("Mapper", "ExtMapper");
    }

    private String getMyBatis3XmlMapperPackage(IntrospectedTable introspectedTable) {
        return introspectedTable.getMyBatis3XmlMapperPackage();
    }


    private void addIfNotExists(CompilationUnit unit, List<GeneratedJavaFile> answer) {
        GeneratedJavaFile generatedJavaFile = new GeneratedJavaFile(unit, context.getJavaClientGeneratorConfiguration().getTargetProject()
                , context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING), context.getJavaFormatter());
        if (notExists(generatedJavaFile)) {
            answer.add(generatedJavaFile);
        }
    }

    private void addIfNotExists(GeneratedXmlFile generatedFile, List<GeneratedXmlFile> answer) {
        if (notExists(generatedFile)) {
            answer.add(generatedFile);
        }
    }

    private Interface createExtMapperInterface(IntrospectedTable introspectedTable) {
        String rootInterface = introspectedTable.getMyBatis3JavaMapperType();
        FullyQualifiedJavaType type = new FullyQualifiedJavaType(
                rootInterface.replace("Mapper", "ExtMapper"));
        Interface interfaze = new Interface(type);
        interfaze.setVisibility(JavaVisibility.PUBLIC);
        FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(
                rootInterface);
        interfaze.addSuperInterface(fqjt);
        interfaze.addImportedType(fqjt);
        return interfaze;
    }

    private Interface createServiceInterface(IntrospectedTable introspectedTable) {
        String fullServiceName = getFullServiceName(introspectedTable);
        FullyQualifiedJavaType type = new FullyQualifiedJavaType(fullServiceName);

        Interface interfaze = new Interface(type);
        interfaze.setVisibility(JavaVisibility.PUBLIC);
        return interfaze;
    }


    private TopLevelClass createServiceImplClass(Interface serviceInterface, Interface extMapperInterface) {

        String fullServiceImplName = getFullServiceImplName(serviceInterface);
        FullyQualifiedJavaType type = new FullyQualifiedJavaType(fullServiceImplName);
        TopLevelClass clazz = new TopLevelClass(type);
        clazz.setVisibility(JavaVisibility.PUBLIC);
        clazz.addSuperInterface(serviceInterface.getType());
        clazz.addImportedType(serviceInterface.getType());

        //增加field
        Field mapperField = new Field();
        mapperField.setVisibility(JavaVisibility.PRIVATE);
        mapperField.setType(extMapperInterface.getType());
        mapperField.setName("mapper");
        clazz.addField(mapperField);
        clazz.addImportedType(extMapperInterface.getType());
        //增加注解
        clazz.addAnnotation("@Service");
        clazz.addImportedType("org.springframework.stereotype.Service");
        return clazz;
    }

    private String getFullServiceName(IntrospectedTable introspectedTable) {
        String servicePackage = getServicePackage();
        String serviceName = getServiceName(introspectedTable);
        return servicePackage + "." + serviceName;
    }

    private String getFullServiceImplName(Interface serviceInterface) {
        String packageName = serviceInterface.getType().getPackageName() + ".impl";
        String serviceImplName = serviceInterface.getType().getShortName() + "Impl";
        return packageName + "." + serviceImplName;
    }

    private String getServicePackage() {
        String mapperPackage = context.getJavaClientGeneratorConfiguration().getTargetPackage();
        return mapperPackage.substring(0, mapperPackage.lastIndexOf('.') + 1) + "service";
    }

    private String getServiceName(IntrospectedTable introspectedTable) {
        String modelName = introspectedTable.getTableConfiguration().getDomainObjectName();
        String serviceName;
        if (modelName.endsWith("Entity")) {
            serviceName = modelName.substring(0, modelName.lastIndexOf("Entity")) + "Service";
        } else if (modelName.endsWith("Model")) {
            serviceName = modelName.substring(0, modelName.lastIndexOf("Model")) + "Service";
        } else {
            serviceName = modelName;
        }
        return serviceName;
    }

    private String getServiceImplName(IntrospectedTable introspectedTable) {
        return getServiceName(introspectedTable) + "Impl";
    }


    private boolean notExists(GeneratedFile generatedFile) {
        File targetFile = getFile(generatedFile.getTargetProject(), generatedFile.getTargetPackage(), generatedFile.getFileName());
        return !targetFile.exists();
    }

    public File getFile(String targetProject, String targetPackage, String fileName) {

        File project = new File(targetProject);
        if (!project.isDirectory()) {
            throw new RuntimeException(getString("Warning.9", //$NON-NLS-1$
                    targetProject));
        }

        StringBuilder sb = new StringBuilder();
        StringTokenizer st = new StringTokenizer(targetPackage, "."); //$NON-NLS-1$
        while (st.hasMoreTokens()) {
            sb.append(st.nextToken());
            sb.append(File.separatorChar);
        }
        sb.append(fileName);

        return new File(project, sb.toString());
    }
}
